/* eslint-disable */
/**
 * http://animejs.com
 * JavaScript animation engine
 * @version v2.2.0
 * @author Julian Garnier
 * @copyright ©2017 Julian Garnier
 * Released under the MIT license
 **/
const def = () => {
  // Defaults

  const defaultInstanceSettings = {
    update: undefined,
    begin: undefined,
    run: undefined,
    complete: undefined,
    loop: 1,
    direction: 'normal',
    autoplay: true,
    offset: 0
  }

  const defaultTweenSettings = {
    duration: 1000,
    delay: 0,
    easing: 'easeOutElastic',
    elasticity: 500,
    round: 0
  }

  const validTransforms = ['translateX', 'translateY', 'translateZ', 'rotate', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scaleX', 'scaleY', 'scaleZ', 'skewX', 'skewY', 'perspective']
  let transformString

  // Utils

  function stringContains(str, text) {
    return str.indexOf(text) > -1
  }

  const is = {
    arr: a => Array.isArray(a),
    obj: a => stringContains(Object.prototype.toString.call(a), 'Object'),
    pth: a => is.obj(a) && a.hasOwnProperty('totalLength'),
    svg: a => a instanceof SVGElement,
    dom: a => a.nodeType || is.svg(a),
    str: a => typeof a === 'string',
    fnc: a => typeof a === 'function',
    und: a => typeof a === 'undefined',
    hex: a => /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(a),
    rgb: a => /^rgb/.test(a),
    hsl: a => /^hsl/.test(a),
    col: a => (is.hex(a) || is.rgb(a) || is.hsl(a))
  }

  // BezierEasing https://github.com/gre/bezier-easing

  const bezier = (() => {
    const kSplineTableSize = 11
    const kSampleStepSize = 1.0 / (kSplineTableSize - 1.0)

    function A(aA1, aA2) {
      return 1.0 - 3.0 * aA2 + 3.0 * aA1
    };

    function B(aA1, aA2) {
      return 3.0 * aA2 - 6.0 * aA1
    };

    function C(aA1) {
      return 3.0 * aA1
    };

    function calcBezier(aT, aA1, aA2) {
      return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT
    };

    function getSlope(aT, aA1, aA2) {
      return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1)
    };

    function binarySubdivide(aX, aA, aB, mX1, mX2) {
      let currentX, currentT, i = 0
      do {
        currentT = aA + (aB - aA) / 2.0
        currentX = calcBezier(currentT, mX1, mX2) - aX
        if (currentX > 0.0) {
          aB = currentT
        } else {
          aA = currentT
        };
      } while (Math.abs(currentX) > 0.0000001 && ++i < 10)
      return currentT
    }

    function newtonRaphsonIterate(aX, aGuessT, mX1, mX2) {
      for (let i = 0; i < 4; ++i) {
        const currentSlope = getSlope(aGuessT, mX1, mX2)
        if (currentSlope === 0.0) return aGuessT
        const currentX = calcBezier(aGuessT, mX1, mX2) - aX
        aGuessT -= currentX / currentSlope
      }
      return aGuessT
    }

    function bezier(mX1, mY1, mX2, mY2) {
      if (!(mX1 >= 0 && mX1 <= 1 && mX2 >= 0 && mX2 <= 1)) return
      let sampleValues = new Float32Array(kSplineTableSize)

      if (mX1 !== mY1 || mX2 !== mY2) {
        for (let i = 0; i < kSplineTableSize; ++i) {
          sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2)
        }
      }

      function getTForX(aX) {
        let intervalStart = 0.0
        let currentSample = 1
        const lastSample = kSplineTableSize - 1

        for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
          intervalStart += kSampleStepSize
        }

        --currentSample

        const dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample])
        const guessForT = intervalStart + dist * kSampleStepSize
        const initialSlope = getSlope(guessForT, mX1, mX2)

        if (initialSlope >= 0.001) {
          return newtonRaphsonIterate(aX, guessForT, mX1, mX2)
        } else if (initialSlope === 0.0) {
          return guessForT
        } else {
          return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2)
        }
      }

      return x => {
        if (mX1 === mY1 && mX2 === mY2) return x
        if (x === 0) return 0
        if (x === 1) return 1
        return calcBezier(getTForX(x), mY1, mY2)
      }
    }

    return bezier

  })()

  const easings = (() => {
    const names = ['Quad', 'Cubic', 'Quart', 'Quint', 'Sine', 'Expo', 'Circ', 'Back', 'Elastic']

    // Elastic easing adapted from jQueryUI http://api.jqueryui.com/easings/

    function elastic(t, p) {
      return t === 0 || t === 1 ? t :
        -Math.pow(2, 10 * (t - 1)) * Math.sin((((t - 1) - (p / (Math.PI * 2.0) * Math.asin(1))) * (Math.PI * 2)) / p)
    }

    // Approximated Penner equations http://matthewlein.com/ceaser/

    const equations = {
      In: [
        [0.550, 0.085, 0.680, 0.530], /* InQuad */
        [0.550, 0.055, 0.675, 0.190], /* InCubic */
        [0.895, 0.030, 0.685, 0.220], /* InQuart */
        [0.755, 0.050, 0.855, 0.060], /* InQuint */
        [0.470, 0.000, 0.745, 0.715], /* InSine */
        [0.950, 0.050, 0.795, 0.035], /* InExpo */
        [0.600, 0.040, 0.980, 0.335], /* InCirc */
        [0.600, -0.280, 0.735, 0.045], /* InBack */
        elastic /* InElastic */
      ],
      Out: [
        [0.250, 0.460, 0.450, 0.940], /* OutQuad */
        [0.215, 0.610, 0.355, 1.000], /* OutCubic */
        [0.165, 0.840, 0.440, 1.000], /* OutQuart */
        [0.230, 1.000, 0.320, 1.000], /* OutQuint */
        [0.390, 0.575, 0.565, 1.000], /* OutSine */
        [0.190, 1.000, 0.220, 1.000], /* OutExpo */
        [0.075, 0.820, 0.165, 1.000], /* OutCirc */
        [0.175, 0.885, 0.320, 1.275], /* OutBack */
        (t, f) => 1 - elastic(1 - t, f) /* OutElastic */
      ],
      InOut: [
        [0.455, 0.030, 0.515, 0.955], /* InOutQuad */
        [0.645, 0.045, 0.355, 1.000], /* InOutCubic */
        [0.770, 0.000, 0.175, 1.000], /* InOutQuart */
        [0.860, 0.000, 0.070, 1.000], /* InOutQuint */
        [0.445, 0.050, 0.550, 0.950], /* InOutSine */
        [1.000, 0.000, 0.000, 1.000], /* InOutExpo */
        [0.785, 0.135, 0.150, 0.860], /* InOutCirc */
        [0.680, -0.550, 0.265, 1.550], /* InOutBack */
        (t, f) => t < 0.5 ? elastic(t * 2, f) / 2 : 1 - elastic(t * -2 + 2, f) / 2 /* InOutElastic */
      ]
    }

    let functions = {
      linear: bezier(0.250, 0.250, 0.750, 0.750)
    }

    for (let type in equations) {
      equations[type].forEach((f, i) => {
        functions['ease' + type + names[i]] = is.fnc(f) ? f : bezier(f)
      })
    }

    return functions

  })()

  // Strings

  function stringToHyphens(str) {
    return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
  }

  function selectString(str) {
    if (is.col(str)) return;
    try {
      let nodes = document.querySelectorAll(str);
      return nodes;
    } catch (e) {

    }
  }

  // Arrays

  function filterArray(arr, callback) {
    const len = arr.length
    const thisArg = arguments.length >= 2 ? arguments[1] : void 0
    let result = []
    for (let i = 0; i < len; i++) {
      if (i in arr) {
        const val = arr[i]
        if (callback.call(thisArg, val, i, arr)) {
          result.push(val)
        }
      }
    }
    return result
  }

  function flattenArray(arr) {
    return arr.reduce((a, b) => a.concat(is.arr(b) ? flattenArray(b) : b), [])
  }

  function toArray(o) {
    if (is.arr(o)) return o
    if (is.str(o)) o = selectString(o) || o
    if (o instanceof NodeList || o instanceof HTMLCollection) return [].slice.call(o)
    return [o]
  }

  function arrayContains(arr, val) {
    return arr.some(a => a === val)
  }

  // Objects

  function cloneObject(o) {
    let clone = {}
    for (let p in o) clone[p] = o[p]
    return clone
  }

  function replaceObjectProps(o1, o2) {
    let o = cloneObject(o1)
    for (let p in o1) o[p] = o2.hasOwnProperty(p) ? o2[p] : o1[p]
    return o
  }

  function mergeObjects(o1, o2) {
    let o = cloneObject(o1)
    for (let p in o2) o[p] = is.und(o1[p]) ? o2[p] : o1[p]
    return o
  }

  // Colors

  function rgbToRgba(rgbValue) {
    const rgb = /rgb\((\d+,\s*[\d]+,\s*[\d]+)\)/g.exec(rgbValue)
    return rgb ? `rgba(${rgb[1]},1)` : rgbValue
  }

  function hexToRgba(hexValue) {
    const rgx = /^#?([a-f\d])([a-f\d])([a-f\d])$/i
    const hex = hexValue.replace(rgx, (m, r, g, b) => r + r + g + g + b + b)
    const rgb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
    const r = parseInt(rgb[1], 16)
    const g = parseInt(rgb[2], 16)
    const b = parseInt(rgb[3], 16)
    return `rgba(${r},${g},${b},1)`
  }

  function hslToRgba(hslValue) {
    const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue) || /hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(hslValue)
    const h = parseInt(hsl[1]) / 360
    const s = parseInt(hsl[2]) / 100
    const l = parseInt(hsl[3]) / 100
    const a = hsl[4] || 1

    function hue2rgb(p, q, t) {
      if (t < 0) t += 1
      if (t > 1) t -= 1
      if (t < 1 / 6) return p + (q - p) * 6 * t
      if (t < 1 / 2) return q
      if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6
      return p
    }
    let r, g, b
    if (s == 0) {
      r = g = b = l
    } else {
      const q = l < 0.5 ? l * (1 + s) : l + s - l * s
      const p = 2 * l - q
      r = hue2rgb(p, q, h + 1 / 3)
      g = hue2rgb(p, q, h)
      b = hue2rgb(p, q, h - 1 / 3)
    }
    return `rgba(${r * 255},${g * 255},${b * 255},${a})`
  }

  function colorToRgb(val) {
    if (is.rgb(val)) return rgbToRgba(val)
    if (is.hex(val)) return hexToRgba(val)
    if (is.hsl(val)) return hslToRgba(val)
  }

  // Units

  function getUnit(val) {
    const split = /([\+\-]?[0-9#\.]+)(%|px|pt|em|rem|in|cm|mm|ex|ch|pc|vw|vh|vmin|vmax|deg|rad|turn)?$/.exec(val)
    if (split) return split[2]
  }

  function getTransformUnit(propName) {
    if (stringContains(propName, 'translate') || propName === 'perspective') return 'px'
    if (stringContains(propName, 'rotate') || stringContains(propName, 'skew')) return 'deg'
  }

  // Values

  function minMaxValue(val, min, max) {
    return Math.min(Math.max(val, min), max)
  }

  function getFunctionValue(val, animatable) {
    if (!is.fnc(val)) return val
    return val(animatable.target, animatable.id, animatable.total)
  }

  function getCSSValue(el, prop) {
    if (prop in el.style) {
      return getComputedStyle(el).getPropertyValue(stringToHyphens(prop)) || '0'
    }
  }

  function getAnimationType(el, prop) {
    if (is.dom(el) && arrayContains(validTransforms, prop)) return 'transform'
    if (is.dom(el) && (el.getAttribute(prop) || (is.svg(el) && el[prop]))) return 'attribute'
    if (is.dom(el) && (prop !== 'transform' && getCSSValue(el, prop))) return 'css'
    if (el[prop] != null) return 'object'
  }

  function getTransformValue(el, propName) {
    const defaultUnit = getTransformUnit(propName)
    const defaultVal = stringContains(propName, 'scale') ? 1 : 0 + defaultUnit
    const str = el.style.transform
    if (!str) return defaultVal
    let match = []
    let props = []
    let values = []
    const rgx = /(\w+)\((.+?)\)/g
    while (match = rgx.exec(str)) {
      props.push(match[1])
      values.push(match[2])
    }
    const value = filterArray(values, (val, i) => props[i] === propName)
    return value.length ? value[0] : defaultVal
  }

  function getOriginalTargetValue(target, propName) {
    switch (getAnimationType(target, propName)) {
      case 'transform':
        return getTransformValue(target, propName)
      case 'css':
        return getCSSValue(target, propName)
      case 'attribute':
        return target.getAttribute(propName)
    }
    return target[propName] || 0
  }

  function getRelativeValue(to, from) {
    const operator = /^(\*=|\+=|-=)/.exec(to)
    if (!operator) return to
    const u = getUnit(to) || 0
    const x = parseFloat(from)
    const y = parseFloat(to.replace(operator[0], ''))
    switch (operator[0][0]) {
      case '+':
        return x + y + u
      case '-':
        return x - y + u
      case '*':
        return x * y + u
    }
  }

  function validateValue(val, unit) {
    if (is.col(val)) return colorToRgb(val)
    const originalUnit = getUnit(val)
    const unitLess = originalUnit ? val.substr(0, val.length - originalUnit.length) : val
    return unit && !/\s/g.test(val) ? unitLess + unit : unitLess
  }

  // getTotalLength() equivalent for circle, rect, polyline, polygon and line shapes.
  // adapted from https://gist.github.com/SebLambla/3e0550c496c236709744

  function getDistance(p1, p2) {
    return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2))
  }

  function getCircleLength(el) {
    return 2 * Math.PI * el.getAttribute('r')
  }

  function getRectLength(el) {
    return (el.getAttribute('width') * 2) + (el.getAttribute('height') * 2)
  }

  function getLineLength(el) {
    return getDistance({
      x: el.getAttribute('x1'),
      y: el.getAttribute('y1')
    }, {
        x: el.getAttribute('x2'),
        y: el.getAttribute('y2')
      })
  }

  function getPolylineLength(el) {
    const points = el.points
    let totalLength = 0
    let previousPos
    for (let i = 0; i < points.numberOfItems; i++) {
      const currentPos = points.getItem(i)
      if (i > 0) totalLength += getDistance(previousPos, currentPos)
      previousPos = currentPos
    }
    return totalLength
  }

  function getPolygonLength(el) {
    const points = el.points
    return getPolylineLength(el) + getDistance(points.getItem(points.numberOfItems - 1), points.getItem(0))
  }

  // Path animation

  function getTotalLength(el) {
    if (el.getTotalLength) return el.getTotalLength()
    switch (el.tagName.toLowerCase()) {
      case 'circle':
        return getCircleLength(el)
      case 'rect':
        return getRectLength(el)
      case 'line':
        return getLineLength(el)
      case 'polyline':
        return getPolylineLength(el)
      case 'polygon':
        return getPolygonLength(el)
    }
  }

  function setDashoffset(el) {
    const pathLength = getTotalLength(el)
    el.setAttribute('stroke-dasharray', pathLength)
    return pathLength
  }

  // Motion path

  function getPath(path, percent) {
    const el = is.str(path) ? selectString(path)[0] : path
    const p = percent || 100
    return function (prop) {
      return {
        el: el,
        property: prop,
        totalLength: getTotalLength(el) * (p / 100)
      }
    }
  }

  function getPathProgress(path, progress) {
    function point(offset = 0) {
      const l = progress + offset >= 1 ? progress + offset : 0
      return path.el.getPointAtLength(l)
    }
    const p = point()
    const p0 = point(-1)
    const p1 = point(+1)
    switch (path.property) {
      case 'x':
        return p.x
      case 'y':
        return p.y
      case 'angle':
        return Math.atan2(p1.y - p0.y, p1.x - p0.x) * 180 / Math.PI
    }
  }

  // Decompose value

  function decomposeValue(val, unit) {
    const rgx = /-?\d*\.?\d+/g
    const value = validateValue((is.pth(val) ? val.totalLength : val), unit) + ''
    return {
      original: value,
      numbers: value.match(rgx) ? value.match(rgx).map(Number) : [0],
      strings: (is.str(val) || unit) ? value.split(rgx) : []
    }
  }

  // Animatables

  function parseTargets(targets) {
    const targetsArray = targets ? (flattenArray(is.arr(targets) ? targets.map(toArray) : toArray(targets))) : []
    return filterArray(targetsArray, (item, pos, self) => self.indexOf(item) === pos)
  }

  function getAnimatables(targets) {
    const parsed = parseTargets(targets)
    return parsed.map((t, i) => {
      return {
        target: t,
        id: i,
        total: parsed.length
      }
    })
  }

  // Properties

  function normalizePropertyTweens(prop, tweenSettings) {
    let settings = cloneObject(tweenSettings)
    if (is.arr(prop)) {
      const l = prop.length
      const isFromTo = (l === 2 && !is.obj(prop[0]))
      if (!isFromTo) {
        // Duration divided by the number of tweens
        if (!is.fnc(tweenSettings.duration)) settings.duration = tweenSettings.duration / l
      } else {
        // Transform [from, to] values shorthand to a valid tween value
        prop = {
          value: prop
        }
      }
    }
    return toArray(prop).map((v, i) => {
      // Default delay value should be applied only on the first tween
      const delay = !i ? tweenSettings.delay : 0
      // Use path object as a tween value
      let obj = is.obj(v) && !is.pth(v) ? v : {
        value: v
      }
      // Set default delay value
      if (is.und(obj.delay)) obj.delay = delay
      return obj
    }).map(k => mergeObjects(k, settings))
  }

  function getProperties(instanceSettings, tweenSettings, params) {
    let properties = []
    const settings = mergeObjects(instanceSettings, tweenSettings)
    for (let p in params) {
      if (!settings.hasOwnProperty(p) && p !== 'targets') {
        properties.push({
          name: p,
          offset: settings['offset'],
          tweens: normalizePropertyTweens(params[p], tweenSettings)
        })
      }
    }
    return properties
  }

  // Tweens

  function normalizeTweenValues(tween, animatable) {
    let t = {}
    for (let p in tween) {
      let value = getFunctionValue(tween[p], animatable)
      if (is.arr(value)) {
        value = value.map(v => getFunctionValue(v, animatable))
        if (value.length === 1) value = value[0]
      }
      t[p] = value
    }
    t.duration = parseFloat(t.duration)
    t.delay = parseFloat(t.delay)
    return t
  }

  function normalizeEasing(val) {
    return is.arr(val) ? bezier(val) : easings[val]
  }

  function normalizeTweens(prop, animatable) {
    let previousTween
    return prop.tweens.map(t => {
      let tween = normalizeTweenValues(t, animatable)
      const tweenValue = tween.value
      const originalValue = getOriginalTargetValue(animatable.target, prop.name)
      const previousValue = previousTween ? previousTween.to.original : originalValue
      const from = is.arr(tweenValue) ? tweenValue[0] : previousValue
      const to = getRelativeValue(is.arr(tweenValue) ? tweenValue[1] : tweenValue, from)
      const unit = getUnit(to) || getUnit(from) || getUnit(originalValue)
      tween.from = decomposeValue(from, unit)
      tween.to = decomposeValue(to, unit)
      tween.start = previousTween ? previousTween.end : prop.offset
      tween.end = tween.start + tween.delay + tween.duration
      tween.easing = normalizeEasing(tween.easing)
      tween.elasticity = (1000 - minMaxValue(tween.elasticity, 1, 999)) / 1000
      tween.isPath = is.pth(tweenValue)
      tween.isColor = is.col(tween.from.original)
      if (tween.isColor) tween.round = 1
      previousTween = tween
      return tween
    })
  }

  // Tween progress

  const setTweenProgress = {
    css: (t, p, v) => t.style[p] = v,
    attribute: (t, p, v) => t.setAttribute(p, v),
    object: (t, p, v) => t[p] = v,
    transform: (t, p, v, transforms, id) => {
      if (!transforms[id]) transforms[id] = []
      transforms[id].push(`${p}(${v})`)
    }
  }

  // Animations

  function createAnimation(animatable, prop) {
    const animType = getAnimationType(animatable.target, prop.name)
    if (animType) {
      const tweens = normalizeTweens(prop, animatable)
      return {
        type: animType,
        property: prop.name,
        animatable: animatable,
        tweens: tweens,
        duration: tweens[tweens.length - 1].end,
        delay: tweens[0].delay
      }
    }
  }

  function getAnimations(animatables, properties) {
    return filterArray(flattenArray(animatables.map(animatable => {
      return properties.map(prop => {
        return createAnimation(animatable, prop)
      })
    })), a => !is.und(a))
  }

  // Create Instance

  function getInstanceTimings(type, animations, instanceSettings, tweenSettings) {
    const isDelay = (type === 'delay')
    if (animations.length) {
      return (isDelay ? Math.min : Math.max).apply(Math, animations.map(anim => anim[type]))
    } else {
      return isDelay ? tweenSettings.delay : instanceSettings.offset + tweenSettings.delay + tweenSettings.duration
    }
  }

  function createNewInstance(params) {
    const instanceSettings = replaceObjectProps(defaultInstanceSettings, params)
    const tweenSettings = replaceObjectProps(defaultTweenSettings, params)
    const animatables = getAnimatables(params.targets)
    const properties = getProperties(instanceSettings, tweenSettings, params)
    const animations = getAnimations(animatables, properties)
    return mergeObjects(instanceSettings, {
      children: [],
      animatables: animatables,
      animations: animations,
      duration: getInstanceTimings('duration', animations, instanceSettings, tweenSettings),
      delay: getInstanceTimings('delay', animations, instanceSettings, tweenSettings)
    })
  }

  // Core

  let activeInstances = []
  let raf = 0

  const engine = (() => {
    function play() {
      raf = requestAnimationFrame(step)
    };

    function step(t) {
      const activeLength = activeInstances.length
      if (activeLength) {
        let i = 0
        while (i < activeLength) {
          if (activeInstances[i]) activeInstances[i].tick(t)
          i++
        }
        play()
      } else {
        cancelAnimationFrame(raf)
        raf = 0
      }
    }
    return play
  })()


  // Public Instance

  function anime(params = {}) {
    let now, startTime, lastTime = 0

    let resolve = null

    function makePromise() {
      return window.Promise && new Promise(_resolve => resolve = _resolve)
    }

    let promise = makePromise()

    let instance = createNewInstance(params)

    function toggleInstanceDirection() {
      instance.reversed = !instance.reversed
    }

    function adjustTime(time) {
      return instance.reversed ? instance.duration - time : time
    }

    function syncInstanceChildren(time) {
      const children = instance.children
      const childrenLength = children.length
      if (time >= instance.currentTime) {
        for (let i = 0; i < childrenLength; i++) children[i].seek(time)
      } else {
        for (let i = childrenLength; i--;) children[i].seek(time)
      }
    }

    function setAnimationsProgress(insTime) {
      let i = 0
      let transforms = {}
      const animations = instance.animations
      const animationsLength = animations.length
      while (i < animationsLength) {
        const anim = animations[i]
        const animatable = anim.animatable
        const tweens = anim.tweens
        const tweenLength = tweens.length - 1
        let tween = tweens[tweenLength]
        // Only check for keyframes if there is more than one tween
        if (tweenLength) tween = filterArray(tweens, t => (insTime < t.end))[0] || tween
        const elapsed = minMaxValue(insTime - tween.start - tween.delay, 0, tween.duration) / tween.duration
        const eased = isNaN(elapsed) ? 1 : tween.easing(elapsed, tween.elasticity)
        const strings = tween.to.strings
        const round = tween.round
        let numbers = []
        let progress
        const toNumbersLength = tween.to.numbers.length
        for (let n = 0; n < toNumbersLength; n++) {
          let value
          const toNumber = tween.to.numbers[n]
          const fromNumber = tween.from.numbers[n]
          if (!tween.isPath) {
            value = fromNumber + (eased * (toNumber - fromNumber))
          } else {
            value = getPathProgress(tween.value, eased * toNumber)
          }
          if (round) {
            if (!(tween.isColor && n > 2)) {
              value = Math.round(value * round) / round
            }
          }
          numbers.push(value)
        }
        // Manual Array.reduce for better performances
        const stringsLength = strings.length
        if (!stringsLength) {
          progress = numbers[0]
        } else {
          progress = strings[0]
          for (let s = 0; s < stringsLength; s++) {
            const a = strings[s]
            const b = strings[s + 1]
            const n = numbers[s]
            if (!isNaN(n)) {
              if (!b) {
                progress += n + ' '
              } else {
                progress += n + b
              }
            }
          }
        }
        setTweenProgress[anim.type](animatable.target, anim.property, progress, transforms, animatable.id)
        anim.currentValue = progress
        i++
      }
      const transformsLength = Object.keys(transforms).length
      if (transformsLength) {
        for (let id = 0; id < transformsLength; id++) {
          if (!transformString) {
            const t = 'transform'
            transformString = (getCSSValue(document.body, t) ? t : `-webkit-${t}`)
          }
          instance.animatables[id].target.style[transformString] = transforms[id].join(' ')
        }
      }
      instance.currentTime = insTime
      instance.progress = (insTime / instance.duration) * 100
    }

    function setCallback(cb) {
      if (instance[cb]) instance[cb](instance)
    }

    function countIteration() {
      if (instance.remaining && instance.remaining !== true) {
        instance.remaining--
      }
    }

    function setInstanceProgress(engineTime) {
      const insDuration = instance.duration
      const insOffset = instance.offset
      const insStart = insOffset + instance.delay
      const insCurrentTime = instance.currentTime
      const insReversed = instance.reversed
      const insTime = adjustTime(engineTime)
      if (instance.children.length) syncInstanceChildren(insTime)
      if (insTime >= insStart || !insDuration) {
        if (!instance.began) {
          instance.began = true
          setCallback('begin')
        }
        setCallback('run')
      }
      if (insTime > insOffset && insTime < insDuration) {
        setAnimationsProgress(insTime)
      } else {
        if (insTime <= insOffset && insCurrentTime !== 0) {
          setAnimationsProgress(0)
          if (insReversed) countIteration()
        }
        if ((insTime >= insDuration && insCurrentTime !== insDuration) || !insDuration) {
          setAnimationsProgress(insDuration)
          if (!insReversed) countIteration()
        }
      }
      setCallback('update')
      if (engineTime >= insDuration) {
        if (instance.remaining) {
          startTime = now
          if (instance.direction === 'alternate') toggleInstanceDirection()
        } else {
          instance.pause()
          if (!instance.completed) {
            instance.completed = true
            setCallback('complete')
            if ('Promise' in window) {
              resolve()
              promise = makePromise()
            }
          }
        }
        lastTime = 0
      }
    }

    instance.reset = function () {
      const direction = instance.direction
      const loops = instance.loop
      instance.currentTime = 0
      instance.progress = 0
      instance.paused = true
      instance.began = false
      instance.completed = false
      instance.reversed = direction === 'reverse'
      instance.remaining = direction === 'alternate' && loops === 1 ? 2 : loops
      setAnimationsProgress(0)
      for (let i = instance.children.length; i--;) {
        instance.children[i].reset()
      }
    }

    instance.tick = function (t) {
      now = t
      if (!startTime) startTime = now
      const engineTime = (lastTime + now - startTime) * anime.speed
      setInstanceProgress(engineTime)
    }

    instance.seek = function (time) {
      setInstanceProgress(adjustTime(time))
    }

    instance.pause = function () {
      const i = activeInstances.indexOf(instance)
      if (i > -1) activeInstances.splice(i, 1)
      instance.paused = true
    }

    instance.play = function () {
      if (!instance.paused) return
      instance.paused = false
      startTime = 0
      lastTime = adjustTime(instance.currentTime)
      activeInstances.push(instance)
      if (!raf) engine()
    }

    instance.reverse = function () {
      toggleInstanceDirection()
      startTime = 0
      lastTime = adjustTime(instance.currentTime)
    }

    instance.restart = function () {
      instance.pause()
      instance.reset()
      instance.play()
    }

    instance.finished = promise

    instance.reset()

    if (instance.autoplay) instance.play()

    return instance

  }

  // Remove targets from animation

  function removeTargets(targets) {
    const targetsArray = parseTargets(targets)
    for (let i = activeInstances.length; i--;) {
      const instance = activeInstances[i]
      const animations = instance.animations
      for (let a = animations.length; a--;) {
        if (arrayContains(targetsArray, animations[a].animatable.target)) {
          animations.splice(a, 1)
          if (!animations.length) instance.pause()
        }
      }
    }
  }

  // Timeline

  function timeline(params) {
    let tl = anime(params)
    tl.pause()
    tl.duration = 0
    tl.add = function (instancesParams) {
      tl.children.forEach(i => {
        i.began = true
        i.completed = true
      })
      toArray(instancesParams).forEach(instanceParams => {
        let insParams = mergeObjects(instanceParams, replaceObjectProps(defaultTweenSettings, params || {}))
        insParams.targets = insParams.targets || params.targets
        const tlDuration = tl.duration
        const insOffset = insParams.offset
        insParams.autoplay = false
        insParams.direction = tl.direction
        insParams.offset = is.und(insOffset) ? tlDuration : getRelativeValue(insOffset, tlDuration)
        tl.began = true
        tl.completed = true
        tl.seek(insParams.offset)
        const ins = anime(insParams)
        ins.began = true
        ins.completed = true
        if (ins.duration > tlDuration) tl.duration = ins.duration
        tl.children.push(ins)
      })
      tl.seek(0)
      tl.reset()
      if (tl.autoplay) tl.restart()
      return tl
    }
    return tl
  }

  anime.version = '2.2.0'
  anime.speed = 1
  anime.running = activeInstances
  anime.remove = removeTargets
  anime.getValue = getOriginalTargetValue
  anime.path = getPath
  anime.setDashoffset = setDashoffset
  anime.bezier = bezier
  anime.easings = easings
  anime.timeline = timeline
  anime.random = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min

  return anime

}
export default def()
